package com.longlinkislong.util;

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
	private DataInputStream dataInputStream;
	private int byteCount;
	
	public LittleEndianDataInputStream(InputStream inputStream) {
		super(inputStream);
		
		this.dataInputStream = new DataInputStream(inputStream);
	}	
	public int getByteCount(){
		return this.byteCount;
	}
	public int read() throws IOException {
		this.byteCount++;
		return super.read();
	}
	private int readCheckEOF() throws IOException {
		final int byteRead = this.read();
		
		if(byteRead == -1)
			throw new EOFException();
		return byteRead;
	}
	public int readUnsignedByte() throws IOException {
		return (this.readCheckEOF() & 0xFF);
	}
	public short readShort() throws IOException {
		final int byte1 = (readCheckEOF() & 0xFF);
		final int byte2 = (readCheckEOF() & 0xFF) << 8;
		
		return ((short) (byte1 | byte2));
	}
	public int readUnsignedShort() throws IOException {
		final int byte1 = (this.readCheckEOF() & 0xFF);
		final int byte2 = (this.readCheckEOF() & 0xFF) << 8;
		
		return (byte1 | byte2);
	}
	public int readInt() throws IOException {
		return (this.readCheckEOF() & 0xFF) |
				(this.readCheckEOF() & 0xFF) << 8 |
				(this.readCheckEOF() & 0xFF) << 16 |
				(this.readCheckEOF() & 0xFF) << 24;
	}
	public long readUnsignedInt() throws IOException{
		final int byte1 = (this.readCheckEOF() & 0xFF);
		final int byte2 = (this.readCheckEOF() & 0xFF) << 8;
		final int byte3 = (this.readCheckEOF() & 0xFF) << 16;
		final int byte4 = (this.readCheckEOF() & 0xFF) << 24;
		
		return (byte1 | byte2 | byte3 | byte4);
	}
	public float readFloat() throws IOException {
		return Float.intBitsToFloat(this.readInt());
	}
	public long readLong() throws IOException {
		return (
				((long) (this.readCheckEOF() & 0xFF)) |
				((long) (this.readCheckEOF() & 0xFF) << 8) |
				((long) (this.readCheckEOF() & 0xFF) << 16) |
				((long) (this.readCheckEOF() & 0xFF) << 24) |
				((long) (this.readCheckEOF() & 0xFF) << 32) |
				((long) (this.readCheckEOF() & 0xFF) << 40) |
				((long) (this.readCheckEOF() & 0xFF) << 48) |
				((long) (this.readCheckEOF() & 0xFF) << 56)
				);
	}
	public double readDouble() throws IOException {
		return Double.longBitsToDouble(this.readLong());
	}
	public void readFully(byte[] b) throws IOException {
		this.readFully(b, 0, b.length);
	}
	public void readFully(byte[] b, int off, int len) throws IOException {
		while(len > 0){
			final int bytesRead = read(b, off, len);
			if(bytesRead == -1){
				throw new EOFException("Unexpected end of file");				
			}else{
				len -= bytesRead;
				off += bytesRead;
			}
		}
	}
	public int skipBytes(int n) throws IOException {
		int bytesRead = 0;
		while(bytesRead < n) {
			final int read = this.read();
			
			if(read == -1){
				break;
			}else {
				bytesRead++;
			}
		}
		return bytesRead;
	}
	public boolean readBoolean() throws IOException {
		return this.readInt() != 0;
	}
	public byte readByte() throws IOException {
		return (byte) this.readCheckEOF();
	}
	public char readChar() throws IOException {
		return (char) ((this.readCheckEOF() << 8) | (this.readCheckEOF() & 0xFF));
	}
	@SuppressWarnings("deprecation")
	public String readLine() throws IOException{
		return this.dataInputStream.readLine();
	}
	public String readUTF() throws IOException {
		return this.dataInputStream.readUTF();
	}
	public synchronized void mark(int readLimit){}
	public boolean markSupported(){
		return false;
	}
	public synchronized void reset() throws IOException{
		throw new IOException();
	}
}
